// Time interval for updating a position of a node in the X-List
// (can be adjusted by the user, depending on the nodes mobility). /* VAL NAUMOV */
#define XLIST_POSITION_UPDATE_INTERVAL 1.0 //seconds


#include "mrmc_mesh_channel.h"
#include <float.h>

#include "trace.h"
#include "delay.h"
#include "object.h"
#include "packet.h"
#include "mac.h"
#include "lib/bsd-list.h"
#include "phy.h"
#include "wireless-phy.h"
#include "mobilenode.h"
#include "ip.h"
#include "dsr/hdr_sr.h"
#include "gridkeeper.h"
#include "tworayground.h"
#include <mesh_mac.h>


static double d2(double x1, double x2, double y1, double y2)
{
  return ((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}

//建立影像对象
static class MrmcMeshChannelClass : public TclClass {
public:
        MrmcMeshChannelClass() : TclClass("Channel/MrmcMeshChannel") {}
        TclObject* create(int, const char*const*) {
                return (new MrmcMeshChannel);
        }
} class_mrmc_mesh_channel;

//NS 初始化
// Wireless mesh extensions
class MobileNode;

double MrmcMeshChannel::highestAntennaZ_ = -1; // i.e., uninitialized
double MrmcMeshChannel::distCST_ = -1;

MrmcMeshChannel::MrmcMeshChannel(void) : Channel(), numNodes_(0), 
					 xListHead_(NULL), sorted_(0) {}

int MrmcMeshChannel::command(int argc, const char*const* argv)
{
	
	if (argc == 3) {
		TclObject *obj;

		if( (obj = TclObject::lookup(argv[2])) == 0) {
			fprintf(stderr, "%s lookup failed\n", argv[1]);
			return TCL_ERROR;
		}
		if (strcmp(argv[1], "add-node") == 0) {
			addNodeToList((MobileNode*) obj);
			return TCL_OK;
		}
		else if (strcmp(argv[1], "remove-node") == 0) {
			removeNodeFromList((MobileNode*) obj);
			return TCL_OK;
		}
		else if ( strcmp (argv[1], "addphy") == 0 ) {
		phy_ = (MeshPhy*) TclObject::lookup (argv[2]);

		return TCL_OK;
		}
	}
	return Channel::command(argc, argv);
}

void
MrmcMeshChannel::sendUp(Packet* p, Phy *tifp)
{
	Scheduler &s = Scheduler::instance();
	Phy *rifp = ifhead_.lh_first;
	Node *tnode = tifp->node();
	Node *rnode = 0;
	Packet *newp;
	double propdelay = 0.0;
	struct hdr_cmn *hdr = HDR_CMN(p);

         /* list-based improvement */
         if(highestAntennaZ_ == -1) {
                 fprintf(stdout, "channel.cc:sendUp - Calc highestAntennaZ_ and distCST_\n");
                 calcHighestAntennaZ(tifp);
                 fprintf(stdout, "highestAntennaZ_ = %0.1f,  distCST_ = %0.1f\n", highestAntennaZ_, distCST_);
         }
	
	 hdr->direction() = hdr_cmn::UP;

	 // still keep grid-keeper around ??
	 if (GridKeeper::instance()) {
	    int i;
	    GridKeeper* gk = GridKeeper::instance();
	    int size = gk->size_; 
	    
	    MobileNode **outlist = new MobileNode *[size];
	 
       	    int out_index = gk->get_neighbors((MobileNode*)tnode,
						         outlist);
	    for (i=0; i < out_index; i ++) {
		
		  newp = p->copy();
		  
		  hdr_meshmac *hdr_ = HDR_MESHMAC(newp);		// 3.11
		  MeshBurst*  burst = hdr_->burst_ ;
		  burst->add_ref ();				// 3.11
		  
//	  	  std::list<W_MeshPdu*>::iterator it;
//		  for ( it = burst->pdus_.begin() ; it != burst->pdus_.end() ; ++it ) {

//			(*it)->sdu()->ip ()->add_ref();
//		  }		// 3.11

		  rnode = outlist[i];
		  propdelay = get_pdelay(tnode, rnode);

		  rifp = (rnode->ifhead()).lh_first; 
		  for(; rifp; rifp = rifp->nextnode()) {
			  if (rifp->channel() == this) {
				s.schedule(rifp, newp, propdelay); 
				//phy_->recv(newp,(Handler*)0);
				 break;
			  }
		  }
 	    }
	    delete [] outlist; 
	 
	 } else { // use list-based improvement
	 
		 MobileNode *mtnode = (MobileNode *) tnode;
		 MobileNode **affectedNodes;// **aN;
		 int numAffectedNodes = -1, i;
		 
		 if(!sorted_){
			 sortLists();
		 }
		 
		 affectedNodes = getAffectedNodes(mtnode, distCST_ + /* safety */ 5, &numAffectedNodes);
//		 int num = 0; //test
		 for (i=0; i < numAffectedNodes; i++) {
			 rnode = affectedNodes[i];
			 
			 if(rnode == tnode)
				 continue;

						 
			 newp = p->copy();
			
			 hdr_meshmac *hdr_ = HDR_MESHMAC(newp);		// 3.11
		  	 MeshBurst*  burst = hdr_->burst_ ;
		  	 burst->add_ref ();
//			 Packet::add();				// 3.11	 

//		  	 std::list<W_MeshPdu*>::iterator it;
//			 for ( it = burst->pdus_.begin() ; it != burst->pdus_.end() ; ++it ) {
//
//				(*it)->sdu()->ip ()->add_ref();
//			 }

			 propdelay = get_pdelay(tnode, rnode);
			 
			 rifp = (rnode->ifhead()).lh_first;

//			 int num = 0 ;	//test
			 for(; rifp; rifp = rifp->nextnode()){
			 
			 
			 	/*
				 * Modified by jt.zheng for mrmc 2010/12/03
				 * the dest rifp must link the same channel
				 */
				if( rifp->channel() == this ){
					s.schedule(rifp, newp, propdelay);
				}
				//s.schedule(rifp, newp, propdelay);
				//END jt.zheng
				
//				phy_->recv(newp,(Handler*)0);
//				++num;
//				if( num == 1 ) { printf (" 节点有1个接口") ; }
			 }
		 }
		 delete [] affectedNodes;
	 }

	
	 hdr_meshmac *hdr_ = HDR_MESHMAC(p);		// 3.11
	 MeshBurst*  burst = hdr_->burst_ ;
	 burst->free();	
	 Packet::free(p);
}


void
MrmcMeshChannel::addNodeToList(MobileNode *mn)
{
	MobileNode *tmp;

	// create list of mobilenodes for this channel
	if (xListHead_ == NULL) {
		fprintf(stderr, "INITIALIZE THE LIST xListHead\n");
		xListHead_ = mn;
		xListHead_->nextXs_[this->index()] = NULL;
		xListHead_->prevXs_[this->index()] = NULL;
	} else {
		for (tmp = xListHead_; tmp->nextXs_[this->index()] != NULL; tmp=tmp->nextXs_[this->index()]);
		tmp->nextXs_[this->index()] = mn;
		mn->prevXs_[this->index()] = tmp;
		mn->nextXs_[this->index()] = NULL;
	}
	numNodes_++;
}

void
MrmcMeshChannel::removeNodeFromList(MobileNode *mn) {
	
	MobileNode *tmp;
	// Find node in list
	for (tmp = xListHead_; tmp->nextXs_[this->index()] != NULL; tmp=tmp->nextXs_[this->index()]) {
		if (tmp == mn) {
			if (tmp == xListHead_) {
				xListHead_ = tmp->nextXs_[this->index()];
				if (tmp->nextXs_[this->index()] != NULL)
					tmp->nextXs_[this->index()]->prevXs_[this->index()] = NULL;
			} else if (tmp->nextXs_[this->index()] == NULL) 
				tmp->prevXs_[this->index()]->nextXs_[this->index()] = NULL;
			else {
				tmp->prevXs_[this->index()]->nextXs_[this->index()] = tmp->nextXs_[this->index()];
				tmp->nextXs_[this->index()]->prevXs_[this->index()] = tmp->prevXs_[this->index()];
			}
			numNodes_--;
			return;
		}
	}
	fprintf(stderr, "Channel: node not found in list\n");
}

void
MrmcMeshChannel::sortLists(void) {
	bool flag = true;
	MobileNode *m, *q;

	sorted_ = true;
	
	fprintf(stderr, "SORTING LISTS ...");
	/* Buble sort algorithm */
	// SORT x-list
	while(flag) {
		flag = false;
		m = xListHead_;
		while (m != NULL){
			if(m->nextXs_[this->index()] != NULL)
				if ( m->X() > m->nextXs_[this->index()]->X() ){
					flag = true;
					//delete_after m;
					q = m->nextXs_[this->index()];
					m->nextXs_[this->index()] = q->nextXs_[this->index()];
					if (q->nextXs_[this->index()] != NULL)
						q->nextXs_[this->index()]->prevXs_[this->index()] = m;
			    
					//insert_before m;
					q->nextXs_[this->index()] = m;
					q->prevXs_[this->index()] = m->prevXs_[this->index()];
					m->prevXs_[this->index()] = q;
					if (q->prevXs_[this->index()] != NULL)
						q->prevXs_[this->index()]->nextXs_[this->index()] = q;

					// adjust Head of List
					if(m == xListHead_) 
						xListHead_ = m->prevXs_[this->index()];
				}
			m = m -> nextXs_[this->index()];
		}
	}
	
	fprintf(stderr, "DONE!\n");
}

void
MrmcMeshChannel::updateNodesList(class MobileNode *mn, double oldX) {
	
	MobileNode* tmp;
	double X = mn->X();
	bool skipX=false;
	
	if(!sorted_) {
		sortLists();
		return;
	}
	
	/* xListHead cannot be NULL here (they are created during creation of mobilenode) */
	
	/***  DELETE ***/
	// deleting mn from x-list
	if(mn->nextXs_[this->index()] != NULL) {
		if(mn->prevXs_[this->index()] != NULL){
			if((mn->nextXs_[this->index()]->X() >= X) && (mn->prevXs_[this->index()]->X() <= X)) skipX = true; // the node doesn't change its position in the list
			else{
				mn->nextXs_[this->index()]->prevXs_[this->index()] = mn->prevXs_[this->index()];
				mn->prevXs_[this->index()]->nextXs_[this->index()] = mn->nextXs_[this->index()];
			}
		}
		
		else{
			if(mn->nextXs_[this->index()]->X() >= X) skipX = true; // skip updating the first element
			else{
				mn->nextXs_[this->index()]->prevXs_[this->index()] = NULL;
				xListHead_ = mn->nextXs_[this->index()];
			}
		}
	}
	
	else if(mn->prevXs_[this->index()] !=NULL){
		if(mn->prevXs_[this->index()]->X() <= X) skipX = true; // skip updating the last element
		else mn->prevXs_[this->index()]->nextXs_[this->index()] = NULL;
	}
	
	if ((mn->prevXs_[this->index()] == NULL) && (mn->nextXs_[this->index()] == NULL)) skipX = true; //skip updating if only one element in list
	
	/*** INSERT ***/
	//inserting mn in x-list
	if(!skipX){
		if(X > oldX){			
			for(tmp = mn; tmp->nextXs_[this->index()] != NULL && tmp->nextXs_[this->index()]->X() < X; tmp = tmp->nextXs_[this->index()]);
			//fprintf(stdout,"Scanning the element addr %d X=%0.f, next addr %d X=%0.f\n", tmp, tmp->X(), tmp->nextXs_[this->index()], tmp->nextXs_[this->index()]->X());
			if(tmp->nextXs_[this->index()] == NULL) { 
				//fprintf(stdout, "tmp->nextXs_[this->index()] is NULL\n");
				tmp->nextXs_[this->index()] = mn;
				mn->prevXs_[this->index()] = tmp;
				mn->nextXs_[this->index()] = NULL;
			} 
			else{ 
				//fprintf(stdout, "tmp->nextXs_[this->index()] is not NULL, tmp->nextXs_[this->index()]->X()=%0.f\n", tmp->nextXs_[this->index()]->X());
				mn->prevXs_[this->index()] = tmp->nextXs_[this->index()]->prevXs_[this->index()];
				mn->nextXs_[this->index()] = tmp->nextXs_[this->index()];
				tmp->nextXs_[this->index()]->prevXs_[this->index()] = mn;  	
				tmp->nextXs_[this->index()] = mn;
			} 
		}
		else{
			for(tmp = mn; tmp->prevXs_[this->index()] != NULL && tmp->prevXs_[this->index()]->X() > X; tmp = tmp->prevXs_[this->index()]);
				//fprintf(stdout,"Scanning the element addr %d X=%0.f, prev addr %d X=%0.f\n", tmp, tmp->X(), tmp->prevXs_[this->index()], tmp->prevXs_[this->index()]->X());
			if(tmp->prevXs_[this->index()] == NULL) {
				//fprintf(stdout, "tmp->prevXs_[this->index()] is NULL\n");
				tmp->prevXs_[this->index()] = mn;
				mn->nextXs_[this->index()] = tmp;
				mn->prevXs_[this->index()] = NULL;
				xListHead_ = mn;
			} 
			else{
				//fprintf(stdout, "tmp->prevXs_[this->index()] is not NULL, tmp->prevXs_[this->index()]->X()=%0.f\n", tmp->prevXs_[this->index()]->X());
				mn->nextXs_[this->index()] = tmp->prevXs_[this->index()]->nextXs_[this->index()];
				mn->prevXs_[this->index()] = tmp->prevXs_[this->index()];
				tmp->prevXs_[this->index()]->nextXs_[this->index()] = mn;  	
				tmp->prevXs_[this->index()] = mn;		
			}
		}
	}
}


MobileNode **
MrmcMeshChannel::getAffectedNodes(MobileNode *mn, double radius,
				  int *numAffectedNodes)
{
	double xmin, xmax, ymin, ymax, mnx, mny, sqmnr, mnr;
	int n = 0;
	MobileNode *tmp, **list, **tmpList, **t_list;

	if (xListHead_ == NULL) {
		*numAffectedNodes=-1;
		fprintf(stderr, "xListHead_ is NULL when trying to send!!!\n");
		return NULL;
	}
	
	xmin = mn->X() - radius;
	xmax = mn->X() + radius;
	ymin = mn->Y() - radius;
	ymax = mn->Y() + radius;
	mnx = mn->X();
	mny = mn->Y();
	sqmnr = radius * radius ;
//	mnr = mn->radius();
	
	// First allocate as much as possibly needed
	tmpList = new MobileNode*[numNodes_];

// by lhz 11.25
	
	for(tmp = xListHead_; tmp != NULL; tmp = tmp->nextXs_[this->index()]) tmpList[n++] = tmp;
	for(int i = 0; i < n; ++i)
		if(tmpList[i]->speed()!=0.0 && (Scheduler::instance().clock() -
						tmpList[i]->getUpdateTime()) > XLIST_POSITION_UPDATE_INTERVAL )
			tmpList[i]->update_position();

///////////////////////////////////////////////////////////////////////////////
	n=0;
	
	for(tmp = mn; tmp != NULL && tmp->X() >= xmin; tmp=tmp->prevXs_[this->index()])
		if(tmp->Y() >= ymin && tmp->Y() <= ymax){
			tmpList[n++] = tmp;
		}
	for(tmp = mn->nextXs_[this->index()]; tmp != NULL && tmp->X() <= xmax; tmp=tmp->nextXs_[this->index()]){
		if(tmp->Y() >= ymin && tmp->Y() <= ymax){
			tmpList[n++] = tmp;
		}
	}


	
	list = new MobileNode*[n];
	memcpy(list, tmpList, n * sizeof(MobileNode *));
	delete [] tmpList;
         
	*numAffectedNodes = n;
	return list;
////////////////////////////////////////////////////////////////////////////////////

/* 以圆为范围
	int num = 0;	//接收到的节点数目；
	t_list = new MobileNode*[numNodes_];

	for(tmp = xListHead_; tmp != NULL; tmp = tmp->nextXs_[this->index()]) tmpList[n++] = tmp;
	for(int i = 0; i < n; ++i) {
		double t1 = tmpList[i]->getUpdateTime() ;
		if(tmpList[i]->speed()!=0.0 && (Scheduler::instance().clock() -
						tmpList[i]->getUpdateTime()) > XLIST_POSITION_UPDATE_INTERVAL )
			tmpList[i]->update_position();
	}

	for(int i = 0; i < n; ++i) {
		double x1 = tmpList[i]->X();
		double y1 = tmpList[i]->Y();
		
		if (d2(tmpList[i]->X(), mnx, tmpList[i]->Y(), mny) < sqmnr) {
			t_list[num++] = tmpList[i];
		}
	}	

	list = new MobileNode*[num];
	memcpy(list, t_list, num * sizeof(MobileNode *));

	delete [] tmpList;
	delete [] t_list;
         
	*numAffectedNodes = num;
	return list;

*/		


}
 


/* Only to be used with mobile nodes (WirelessPhy).
 * NS-2 at its current state support only a flat (non 3D) movement of nodes,
 * so we assume antenna heights do not change for the dureation of
 * a simulation.
 * Another assumption - all nodes have the same wireless interface, so that
 * the maximum distance, corresponding to CST (at max transmission power 
 * level) stays the same for all nodes.
 */
void
MrmcMeshChannel::calcHighestAntennaZ(Phy *tifp)
{
       double highestZ = 0;
       Phy *n;
 
       for(n = ifhead_.lh_first; n; n = n->nextchnl()) {
                   if(((WirelessPhy *)n)->getAntennaZ() > highestZ)
                               highestZ = ((WirelessPhy *)n)->getAntennaZ();
       }
 
       highestAntennaZ_ = highestZ;

       WirelessPhy *wifp = (WirelessPhy *)tifp;
       distCST_ = wifp->getDist(wifp->getCSThresh(), wifp->getPt(), 1.0, 1.0,
				highestZ , highestZ, wifp->getL(),
				wifp->getLambda());       
}

	
double
MrmcMeshChannel::get_pdelay(Node* tnode, Node* rnode)
{
	// Scheduler	&s = Scheduler::instance();
	MobileNode* tmnode = (MobileNode*)tnode;
	MobileNode* rmnode = (MobileNode*)rnode;
	double propdelay = 0;
	
	propdelay = tmnode->propdelay(rmnode);

	assert(propdelay >= 0.0);
	if (propdelay == 0.0) {
		/* if the propdelay is 0 b/c two nodes are on top of 
		   each other, move them slightly apart -dam 7/28/98 */
		propdelay = 2 * DBL_EPSILON;
		//printf ("propdelay 0: %d->%d at %f\n",
		//	tmnode->address(), rmnode->address(), s.clock());
	}
	return propdelay;
}



